package org.moyrax.compressor;

import java.io.File;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.shared.model.fileset.FileSet;
import org.apache.maven.shared.model.fileset.util.FileSetManager;
import org.mozilla.javascript.ErrorReporter;

/**
 * Compress a set of JavaScript and CSS files using the YUI Compressor, and
 * allows to perform several transformations in files when it's compressing.
 *
 * @goal compress
 * @phase process-resources
 *
 * @author Matias Mirabelli <lumen.night@gmail.com>
 */
public class CompressorPlugin extends AbstractMojo {
  /**
   * Error reporter to notify errors thrown by this compressor.
   */
  private final ErrorReporter errorReporter = new CompressorErrorReporter();

  /**
   * Source files to be compressed.
   *
   * @parameter
   * @required
   */
  private FileSet sourceDirectory;

  /**
   * Source files to be compressed.
   *
   * @parameter default-value="-min"
   */
  private String outputFilesSuffix;

  /**
   * Object to ask the files specified in the plugin configuration.
   */
  private final FileSetManager fileSetManager = new FileSetManager();

  /**
   * Some source control tools don't like files containing lines longer than,
   * say 8000 characters. The linebreak option is used in that case to split
   * long lines after a specific column. It can also be used to make the code
   * more readable, easier to debug (especially with the MS Script Debugger)
   * Specify 0 to get a line break after each semi-colon in JavaScript, and
   * after each rule in CSS.
   *
   * @parameter default-value="-1"
   */
  private int linebreak;

  /**
   * Sets or returns if the compressed output should be obfuscated. By default,
   * the output is obfuscated.
   *
   * @parameter default-value="true"
   */
  private boolean munge;

  /**
   * Sets or returns if the compressor will log informational messages and
   * warnings. By default, no additional information will be logged.
   *
   * @parameter default-value="false"
   */
  private boolean verbose;

  /**
   * Sets or returns if the compressor will preserve unnecessary semicolons
   * (such as right before a '}') This option is useful when compressed code
   * has to be run through JSLint (which is the case of YUI for example).
   *
   * @parameter default-value="false"
   */
  private boolean preserveAllSemiColons;

  /**
   * Disable all the built-in micro optimizations.
   *
   * @parameter default-value="false"
   */
  private boolean disableOptimizations;


  /**
   * Executes this plugin.
   */
  public void execute() throws MojoExecutionException {
    try {
      String[] includes = fileSetManager.getIncludedFiles(sourceDirectory);

      this.compress(includes);
    } catch ( Exception e ) {
      throw new MojoExecutionException("Plugin exception.", e);
    }
  }

  /**
   * Compress the specified files.
   *
   * @param includes  Files to be compressed.
   */
  private void compress (final String[] includes) {
    final String directory = sourceDirectory.getDirectory();

    for (int i = 0; i < includes.length; i++) {
      File file = new File(directory + "/" + includes[i]);

      final Compressor compressor = getCompressor();

      compressor.compress(file, this.getOutputFileFor(file));
    }
  }

  /**
   * Returns a new compressor with the pom-defined values.
   */
  private Compressor getCompressor() {
    DefaultCompressor compressor = new DefaultCompressor(errorReporter);

    compressor.disableOptimizations = disableOptimizations;
    compressor.linebreak = linebreak;
    compressor.munge = munge;
    compressor.preserveAllSemiColons = preserveAllSemiColons;
    compressor.verbose = verbose;

    return compressor;
  }

  /**
   * Returns the output file from a specified input file.
   *
   * @param inputFile  Any input file.
   */
  private File getOutputFileFor(final File inputFile) {
    String path = inputFile.getAbsolutePath();
    int dotPos = path.lastIndexOf(".");

    /* Checks if the file extension dot exists. */
    if (dotPos == -1) {
      dotPos = path.length();
    }

    String extension = path.substring(dotPos);
    String filename = path.substring(0, dotPos);

    File outputFile = new File(filename + outputFilesSuffix + extension);

    return outputFile;
  }
}
